from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.template import RequestContext
from django.contrib.auth import logout, login, authenticate
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.gis.geos import fromstr
from django.contrib.gis.measure import D # D is a short for Distance
from django.shortcuts import render_to_response, get_object_or_404
from datetime import datetime

from reviews.models import Dish, Place, Review, DishImage
from reviews.forms import ReviewForm, RegistrationForm, UploadDishPhotoForm, SearchForm, EditTagsForm

from tagging.models import Tag
from tagging.utils import parse_tags, edit_string_for_tags

def add_search_form_processor(request):
    form = SearchForm()
    return {'search_form': form }

def register_page(request):
    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            user = User.objects.create_user(
                username = cd['username'].lower(),
                password = cd['password1'],
                email = cd['email']
            )
            # automagically log user in
            user = authenticate(username = cd['username'].lower(), password = cd['password1'])
            login(request, user)
            return HttpResponseRedirect('/')
    else:
        form = RegistrationForm()
    variables = RequestContext(request, { 'form': form })
    return render_to_response('registration/register.html', variables)

def log_user_out(request):
    logout(request)
    return HttpResponseRedirect('/')

def user_page(request, user_name):
    try:
        user = User.objects.get(username=user_name)
    except User.DoesNotExist:
        raise Http404('Requested user not found.')
    print user.username
    variables = RequestContext(request, {
        'user_public' : user,
        'show_edit' : user_name == request.user.username,
    })
    return render_to_response('reviews/user_page.html', variables)

def home(request):
    '''List all dishes and their corresponding tags.'''
    form = SearchForm()
    num_latest = 10
    dishes = Dish.objects.all().order_by('-created_date')[:num_latest]
    variables = RequestContext(request, {
        'form': form,
        'dishes': dishes,
        'show_results': False,
        'show_tags': True,
        'show_user': True,
        'show_edit' : request.user.is_authenticated(),
        'num_latest': num_latest,
    })
    return render_to_response('reviews/main_page.html', variables)
	
def search(request):
    form = SearchForm()
    dishes = Dish.objects.all()
    if 'query' in request.GET:
        query = request.GET['query'].strip()
        if query:
            form = SearchForm({'query' : query}) 
            dishes = dishes.filter(name__icontains=query)

    if 'lat' in request.GET and 'lng' in request.GET:
        # filter dishes based on lat, lng
        lat = request.GET['lat'].strip()
        lng = request.GET['lng'].strip()
        if lat and lng:
            # need to populate form with search value like above
            pnt = fromstr('POINT(' + lat + ' ' + lng + ')', srid=4326)
            dishes = dishes.filter(geometry__distance_lte=(pnt, D(mi=10)))

    if 'sort_by' in request.GET:
        # sort by some criteria
        criteria = request.GET['sort_by'].strip()
        if criteria == "distance":
            dishes = dishes.distance(pnt).order_by('distance') # assumes pnt is already created

    variables = RequestContext(request, {
        'form': form,
        'dishes': dishes,
        'show_tags': True,
        'show_user': True,
        'show_edit' : request.user.is_authenticated(),
    })
    if request.GET.has_key('ajax'):
        return render_to_response('reviews/dish_list.html', variables)
    else:
        return render_to_response('reviews/main_page.html', variables)

def tag(request, tag_id):
    '''List all the dishes associated with the specified tag.'''
    tag = get_object_or_404(Tag, id=tag_id)
    related_tags = tag.all_related_tags()
    return render_to_response('reviews/tag.html', RequestContext(request, {'tag' : tag, 'related_tags' : related_tags}))

def place(request, place_slug):
    place = get_object_or_404(Place, slug=place_slug)
    variables = RequestContext(request, {
        'place' : place,
        'show_edit' : request.user.is_authenticated()
    })
    return render_to_response('reviews/place.html', variables)

def dish(request, dish_id):
    '''List all the reviews for the given dish'''
    d = get_object_or_404(Dish, id=dish_id)
    reviews = d.review_set.all()
    avg_rating = None
    if reviews:
        avg_rating = float(sum([rev.rating for rev in reviews]))/len(reviews)
    variables = RequestContext(request, {
        'dish' : d,
        'avg_rating' : avg_rating
    })
    return render_to_response('reviews/dish.html', variables)

@login_required
def add_photo(request, dish_id):
    if dish_id:
        dish = get_object_or_404(Dish, id=dish_id)
    if request.method == 'POST':
        form = UploadDishPhotoForm(request.POST, request.FILES)
        if form.is_valid():
            new_dish_image = form.save(commit=False)
            new_dish_image.dish = dish
            new_dish_image.user = request.user
            new_dish_image.save()
            return HttpResponseRedirect('/dish/' + str(dish.id))
    else:
        form = UploadDishPhotoForm()
    return render_to_response('reviews/upload.html', {'form': form}, context_instance=RequestContext(request))

@login_required
def submit_dish(request):
    dish_id = None
    if request.method == 'POST':
        form = ReviewForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            placename = cd['place']
            place, dummy = Place.objects.get_or_create(name = placename)
            dish, dummy = Dish.objects.get_or_create(name = cd['name'],
                                              place = place)
            dish_id = dish.id
            return submit_review(request, dish_id)
    else:
        initial_data = {'text': 'Write stuff here.'}
        form = ReviewForm(initial = initial_data)
        return render_to_response('reviews/submit_form_page.html', {'form': form},
                                  context_instance=RequestContext(request))

@login_required
def submit_tags(request, dish_id):
    dish = get_object_or_404(Dish, id=dish_id)
    ajax = 'ajax' in request.GET
    initial_data = {}
    if request.method == 'POST':
        form = EditTagsForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
			# parse tags and add to dish
            # what if users are stupid and tag dishes badly?
            tag_string = cd['tags']
            tag_list = parse_tags(tag_string)
            #tag_obj_list = []
            dish.tags.clear()
            for tag in tag_list:
                tag_obj, dummy = Tag.objects.get_or_create(name = tag)
                #tag_obj_list.append(tag_obj)
                if not tag_obj in dish.tags.all():
                    dish.tags.add(tag_obj)
            if ajax:
                variables = RequestContext(request, {
                    'dish': dish,
                    'show_edit': request.user.is_authenticated(),
                    })
                return render_to_response(
                    'reviews/tag_list.html', variables
                )
            
            return HttpResponseRedirect('/dish/' + str(dish.id))
        else:
            if ajax:
                return HttpResponse(u'failure')
    tags_string = edit_string_for_tags(dish.tags.all())
    initial_data['tags'] = tags_string
    initial_data['dish_id'] = dish_id
    form = EditTagsForm(
        initial = initial_data
    )
    if ajax:
        return render_to_response('reviews/edit_tags_form.html', {'form': form},
                                  context_instance=RequestContext(request))

    return render_to_response('reviews/submit_form_page.html', {'form': form},
                              context_instance=RequestContext(request))

@login_required
def delete_review_or_image(request):
    if 'review_id' in request.GET:
        review_id = request.GET['review_id']
        review = get_object_or_404(Review, id=review_id)
        review.delete()
    elif 'photo_id' in request.GET:
        photo_id = request.GET['photo_id']
        photo = get_object_or_404(DishImage, id=photo_id)
        photo.delete()
    return HttpResponseRedirect('/user/' + str(request.user.username))

@login_required
def submit_review(request, dish_id):
    dish = get_object_or_404(Dish, id=dish_id)
    initial_data = {}
    if request.method == 'POST':
        form = ReviewForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            if 'review_id' in cd and cd['review_id']:
                review_id = cd['review_id']
                review = Review.objects.get(id = review_id)
                review.text = cd['text']
                review.rating = cd['rating']
            else:
                review = Review.objects.create(text = cd['text'],
                                           dish = dish,
                                           user = request.user,
                                           rating = cd['rating'])
			# parse tags and add to dish
            # what if users are stupid and tag dishes badly?
            tag_string = cd['tags']
            tag_list = parse_tags(tag_string)
            for tag in tag_list:
                tag_obj, dummy = Tag.objects.get_or_create(name = tag)
                tag_obj.review_set.add(review)
                if not tag_obj in dish.tags.all():
                    dish.tags.add(tag_obj)

            dish.review_set.add(review)
            return HttpResponseRedirect('/dish/' + str(dish.id))
    elif 'review_id' in request.GET: # case for editing an existing review
        review_id = request.GET['review_id']
        try:
            review = Review.objects.get(id = review_id)
            tags_string = edit_string_for_tags(review.tags.all())
            initial_data['tags'] = tags_string
            initial_data['text'] = review.text
            initial_data['rating'] = review.rating
            initial_data['review_id'] = review.id
        except (Review.DoesNotExist):
            pass


    else: # case for new review
        initial_data['text'] = 'Write stuff here.'
    initial_data['name'] = dish.name
    initial_data['place'] = dish.place
 
    form = ReviewForm(
        initial = initial_data
    )
    return render_to_response('reviews/submit_form_page.html', {'form': form},
                              context_instance=RequestContext(request))

# def add_dish(request):
    # #dish = get_object_or_404(Dish, id=dish_id)
    # if request.method == 'POST':
        # form = NewDishForm(request.POST)
        # if form.is_valid():
            # cd = form.cleaned_data
            # dish = Dish.objects.create(name = cd['name'],
                                       # place = cd['place'])
            # review.save()
            # return HttpResponseRedirect('/')
    # else:
        # form = ReviewForm(
            # initial = {'text': 'Write stuff here.'}
        # )
    # return render_to_response('reviews/review.html', {'form': form, 'dish': dish},
                              # context_instance=RequestContext(request))

# def add_review(request, dish_id):
    # dish = get_object_or_404(Dish, id=dish_id)
    # try:
        # review = Review.objects.create(text=str(request.POST['review_text']), dish=dish)
    # except (KeyError):
        # # Redisplay the review form.
        # return render_to_response('reviews/review.html', {
            # 'dish': dish,
            # 'error_message': "You didn't seleAdasdfasfct a choice.",
        # }, context_instance=RequestContext(request))
    # else:
        # review.save()
        # # Always return an HttpResponseRedirect after successfully dealing
        # # with POST data. This prevents data from being posted twice if a
        # # user hits the Back button.
        # return HttpResponseRedirect(reverse('reviews.views.dish', args=(dish.id,)))